package logic

import (
	"core/command"
	"core/common"
	"core/component/function"
	"core/component/logger"
	"core/component/router"
	"core/component/timer"
	"core/logic/logicredis"
	"encoding/json"
	"github.com/golang/protobuf/proto"
	"runtime/debug"
	"time"
)

type CenterMqHandler = func([]byte)
type InnerServerHandler = func([]byte)

type Player struct {
	router             *router.Router
	centerMqHandler    map[command.CenterMQCommand]CenterMqHandler
	innerServerHandler map[command.InnerServerCommand]InnerServerHandler
	playerChanMsg      chan *common.PlayerMsgData
	centerMqCh         chan []byte
	innerServerCh      chan *command.InnServerInfo
	timer              *timer.Timer
	isInitFinish       bool
	chanOffline        chan uint32
	clientAddr         string
	playerId           uint64
	isOffline          bool
	isGotoPvpServer    bool
	gatewaySrvID       string //玩家所在gateway服务器ID
	pvpSrvID           string //玩家所在pvp服务器ID
	user               *UserInfo
}

func newPlayer(playerId uint64, clientAddr, gatewaySrvID string) *Player {
	player := &Player{
		router:             router.NewRouter(),
		centerMqHandler:    make(map[command.CenterMQCommand]CenterMqHandler),
		innerServerHandler: make(map[command.InnerServerCommand]InnerServerHandler),
		playerChanMsg:      make(chan *common.PlayerMsgData, 1024),
		innerServerCh:      make(chan *command.InnServerInfo, 1024),
		centerMqCh:         make(chan []byte, 1024),
		chanOffline:        make(chan uint32, 1),
		gatewaySrvID:       gatewaySrvID,
		clientAddr:         clientAddr,
		playerId:           playerId,
		timer:              timer.NewTimer(),
	}
	player.init()
	go player.loop()
	return player
}

func (player *Player) loop() {
	ticker := time.NewTicker(500 * time.Millisecond)
	if !player.isInitFinish {
		player.isInitFinish = true
	}
	defer func() {
		if err := recover(); err != nil {
			function.WritePanic(string(debug.Stack()), err)
			if player.isInitFinish && !player.isOffline {
				go player.loop()
				return
			}
			player.offline()
		}
	}()
	for {
		select {
		case <-ticker.C:
			player.timer.Loop()
		case msg := <-player.playerChanMsg:
			player.onMessage(msg.Data)
		case data := <-player.centerMqCh:
			player.doCenterMQInfo(data)
		case msg := <-player.innerServerCh:
			player.doRecvOtherPlayerMsg(msg)
		case <-player.chanOffline:
			player.isOffline = true
			player.offline()
			return
		}
	}
}

// 玩家下线
func (player *Player) offline() {
	logicredis.CacheRedis().Del(common.MakePlayerIsOnline(player.playerId))
	player.saveDB()
	msgToPvpServer := &command.PlayerLeavePvpServer{
		PlayerId: player.playerId,
	}
	player.sendMsgToPvp(command.Command_PlayerLeavePvp_Server, msgToPvpServer)
	WorldGetMe().sendRemovePlayer(player.playerId)

	player.offlineClear()
	logger.Infof("玩家(%v)下线", player.playerId)
}

func (player *Player) handleClientMsgPacket(data *common.PlayerMsgData) {
	if player.isOffline {
		return
	}
	player.playerChanMsg <- data
}

func (player *Player) onMessage(data []byte) {
	_, err := player.router.Route(data)
	if err != nil {
		return
	}
}

func (player *Player) init() {
	player.initBaseInfo()
	player.regMsgHandler()
	player.regInnerServerHandler()
	player.registerPlayerTimer()
	player.registerCenterMessageQueueHandler()
	player.processPlayerOnline()
}

func (player *Player) initBaseInfo() {
	player.user = newUserInfo(player)
}

func (player *Player) processPlayerOnline() {
	player.sendClientOnlineToGateway()
}

func (player *Player) sendClientOnlineToGateway() {
	msgSend := &command.ClientOnlineRet{
		UserID: player.playerId,
	}
	player.sendMsgToGateway(command.Command_ClientOnlineRet_Server, msgSend)
}

// 数据转发到gateway
func (player *Player) sendMsgToGateway(cmd command.Command, msg interface{}) bool {
	return gatewayClientMgrGetMe().sendMsgToGateway(player.gatewaySrvID, cmd, msg)
}

func (player *Player) sendMsgToPvp(cmd command.Command, msg interface{}) {
	data, _ := player.router.Marshal(uint16(cmd), msg)
	player.forwardMsgToPvp(data)
}

// 数据转发到pvp
func (player *Player) forwardMsgToPvp(data []byte) {
	pvpClientMgrGetMe().forwardMsgToPvp(player.pvpSrvID, player.playerId, data)
	return
}

func (player *Player) saveDB() {
	player.user.saveDB()
}

// 定时保存数据至mongoDB
func (player *Player) saveMongoDBTimer() {
	player.saveDB()
}

func (player *Player) handleOfflineOP(opType uint32) {
	// 玩家已经开始离线操作了
	if player.isOffline {
		return
	}
	select {
	case player.chanOffline <- opType:
	default:
		logger.Errorf("[消息] 转发消息到player goroutine错误 playerID:%v", player.playerId)
	}
}

func (player *Player) sendMsg(cmd command.Command, msg interface{}) bool {
	return player.SendMsgToClient(cmd, msg)
}

// SendMsgToClient 发送数据给客户端
func (player *Player) SendMsgToClient(cmd command.Command, msg interface{}) bool {
	return gatewayClientMgrGetMe().sendMsgToGatewayClient(player.gatewaySrvID, player.playerId, cmd, msg)
}

// 转发来自pvp服务器的消息给客户端
func (player *Player) sendForwardMsgToClient(data []byte) bool {
	return gatewayClientMgrGetMe().sendRowMsgToGatewayClient(player.gatewaySrvID, player.playerId, data)
}

func (player *Player) playerIntoPvp() {
	pClient := pvpClientMgrGetMe().findAPvpClient()
	if pClient == nil {
		return
	}
	player.pvpSrvID = pClient.serverID
	msgToPvpServer := &command.PlayerIntoPvpServer{
		PlayerId:    player.playerId,
		NickName:    player.user.nickName,
		OnlineSrvID: ServerInstance().id,
	}
	player.sendMsgToPvp(command.Command_PlayerIntoPvp_Server, msgToPvpServer)
}

// 处理收到其他online服玩家所发给自己的消息
func (player *Player) doCenterMQInfo(data []byte) {
	info := &command.CenterMqInfo{}
	_ = proto.Unmarshal(data, info)
	handler, ok := player.centerMqHandler[info.Cmd]
	if !ok {
		return
	}
	handler(info.Data)
	logger.Infof("doCenterMqInfo cmd:%v", info.Cmd.String())
}

// 发送到其他online服玩家
func (player *Player) sendMsgToCenterMq(otherPlayerID uint64, cmd command.CenterMQCommand, msg interface{}) {
	info := &command.CenterMqInfo{}
	info.Cmd = cmd
	info.Data, _ = json.Marshal(msg)

	msgSend := &command.OnlineMsgToCenter{
		Cmd:      command.CenterCommand_PublishToOnlineMsg_Center,
		Topic:    CommonTopic,
		PlayerID: otherPlayerID,
	}
	msgSend.Data, _ = proto.Marshal(info)
	centerClientMgrGetMe().sendMsgToCenterMessageQueue(msgSend)
	logger.Infof("sendMsgToCenterMq otherPlayerID:%v cmd:%v", otherPlayerID, cmd.String())
}

func (player *Player) doRecvOtherPlayerMsg(msg *command.InnServerInfo) {
	handler, ok := player.innerServerHandler[msg.Cmd]
	if !ok {
		return
	}
	handler(msg.Data)
	logger.Infof("doRecvOtherPlayerMsg cmd:%v", msg.Cmd.String())
}

func (player *Player) sendMsgToOtherPlayer(otherPlayerID uint64, cmd command.InnerServerCommand, msg interface{}) {
	info := &command.InnServerInfo{
		Cmd: cmd,
	}
	info.Data, _ = json.Marshal(msg)
	otherPlayer := WorldGetMe().getPlayer(otherPlayerID)
	if player == nil {
		return
	}
	otherPlayer.innerServerCh <- info
	logger.Infof("sendMsgToOtherPlayer otherPlayerID:%v cmd:%v", otherPlayerID, cmd.String())
}

func (player *Player) offlineClear() {
	close(player.playerChanMsg)
	close(player.centerMqCh)
	close(player.innerServerCh)
	close(player.chanOffline)
	player.router.Clear()
	player.router = nil
	player.centerMqHandler = nil
	player.innerServerHandler = nil
	player.user = nil
}
